﻿using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace ProjectTW.Foliage
{
    /// <summary>
    /// 网格管理器
    /// </summary>
    public class FoliageMeshManager : TargetBase
    {
        // 世界坐标id
        private static int s_property_id_worldposition = -1;
        // 草密度图id
        private static int s_property_id_densitymap = -1;
        // 原型属性id
        private static int s_property_id_prototype = -1;

        public static int PropertyIdWorldposition
        {
            get
            {
                if (s_property_id_worldposition == -1)
                {
                    s_property_id_worldposition = Shader.PropertyToID("_WorldPosition");
                }

                return s_property_id_worldposition;
            }
        }

        public static int PropertyIdDensitymap
        {
            get
            {
                if (s_property_id_densitymap == -1)
                {
                    s_property_id_densitymap = Shader.PropertyToID("_DensityMap");
                }

                return s_property_id_densitymap;
            }
        }

        public static int PropertyIdPrototype
        {
            get
            {
                if (s_property_id_prototype == -1)
                {
                    s_property_id_prototype = Shader.PropertyToID("_PrototypeID");
                }

                return s_property_id_prototype;
            }
        }

#if UNITY_EDITOR
        [System.NonSerialized]
        private RenderingQueueReceiver m_EditorQueueInstance = null;
        
        internal RenderingQueueReceiver EditorQueueInstance
        {
            get
            {
                if (m_EditorQueueInstance == null)
                {
                    m_EditorQueueInstance = new RenderingQueueReceiver();
        
                    SceneView sView = SceneView.lastActiveSceneView;
        
                    if (sView == null)
                    {
                        return null;
                    }
        
                    m_EditorQueueInstance.Transform = sView.camera.transform;
                    m_EditorQueueInstance.Camera = sView.camera;
                }
        
                // look for destroyed scene view.
        
                try
                {
                    if (m_EditorQueueInstance.Transform.position.x == 0)
                    {
                    }
                }
                catch
                {
                    m_EditorQueueInstance = null;
        
                    return EditorQueueInstance;
                }
        
                return m_EditorQueueInstance;
            }
            set
            {
                m_EditorQueueInstance = value;
            }
        }
        
        protected bool RENDERING_editorDrawCalled;
        protected Vector3 RENDERING_lastEditorCameraPosition = Vector3.zero;
#endif

        // 构建分比率为类型Mesh索引
        private static Dictionary<FoliageResolutions, Dictionary<int, FoliageGPUMesh>> s_PrototypeMeshInstances = null;
        // 材质属性块
        [System.NonSerialized]
        private MaterialPropertyBlock m_PropertyBlock = null;

        public static Dictionary<FoliageResolutions, Dictionary<int, FoliageGPUMesh>> PrototypeMeshInstances
        {
            get
            {
                if (s_PrototypeMeshInstances == null)
                {
                    s_PrototypeMeshInstances = new Dictionary<FoliageResolutions, Dictionary<int, FoliageGPUMesh>>();
                }

                return s_PrototypeMeshInstances;
            }
        }

        public MaterialPropertyBlock PropertyBlock
        {
            get
            {
                if (m_PropertyBlock == null)
                {
                    m_PropertyBlock = new MaterialPropertyBlock();
                }

                return m_PropertyBlock;
            }
        }

        protected override void OnEnable()
        {
            base.OnEnable();

#if UNITY_EDITOR
            Camera.onPostRender += OnGlobalPostRender;
#endif

#if UNITY_EDITOR
            EditorUtility.SetDirty(this);
#endif
        }

        protected override void OnDisable()
        {
            base.OnDisable();

            DestroyMeshInstances();

#if UNITY_EDITOR
            Camera.onPostRender -= OnGlobalPostRender;
#endif
        }

        /// <summary>
        /// Generate new mesh instances
        /// </summary>
        /// <param name="areaSize"></param>
        public static void GenerateFoliageMeshInstances()
        {
            foreach (var meshInstances in PrototypeMeshInstances)
            {
                if (meshInstances.Value.Count > 0)
                {
                    for (int i = 0; i < FoliageDB.UnSortedPrototypes.Count; i++)
                    {
                        if (meshInstances.Value.ContainsKey(FoliageDB.UnSortedPrototypes[i].Id))
                        {
                            meshInstances.Value[FoliageDB.UnSortedPrototypes[i].Id].Destroy();
                        }
                    }

                    meshInstances.Value.Clear();
                }

                if (FoliageManager.Instance.Enabled)
                {
                    for (int prototypeIndex = 0; prototypeIndex < FoliageDB.UnSortedPrototypes.Count; prototypeIndex++)
                    {
                        if (FoliageDB.UnSortedPrototypes[prototypeIndex].Enabled)
                        {
                            GenerateFoliageMeshInstanceForIndex(FoliageDB.UnSortedPrototypes[prototypeIndex].Id, meshInstances.Key);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 生成网格实例
        /// </summary>
        /// <param name="areaSize"></param>
        public static void GenerateFoliageMeshInstances(FoliageResolutions resolution)
        {
            if (!PrototypeMeshInstances.ContainsKey(resolution))
            {
                PrototypeMeshInstances.Add(resolution, new Dictionary<int, FoliageGPUMesh>());
            }

            Dictionary<int, FoliageGPUMesh> meshInstances = PrototypeMeshInstances[resolution];

            if (meshInstances.Count > 0)
            {
                for (int i = 0; i < FoliageDB.UnSortedPrototypes.Count; i++)
                {
                    if (meshInstances.ContainsKey(FoliageDB.UnSortedPrototypes[i].Id))
                    {
                        meshInstances[FoliageDB.UnSortedPrototypes[i].Id].Destroy();
                    }
                }

                meshInstances.Clear();
            }

            if (FoliageManager.Instance.Enabled)
            {
                for (int prototypeIndex = 0; prototypeIndex < FoliageDB.UnSortedPrototypes.Count; prototypeIndex++)
                {
                    if (FoliageDB.UnSortedPrototypes[prototypeIndex].Enabled)
                    {
                        GenerateFoliageMeshInstanceForIndex(FoliageDB.UnSortedPrototypes[prototypeIndex].Id, resolution);
                    }
                }
            }
        }

        /// <summary>
        /// Generate new mesh instances
        /// </summary>
        /// <param name="areaSize"></param>
        public static void GenerateFoliageMeshInstances(int prototypeId)
        {
            foreach (var meshInstances in PrototypeMeshInstances)
            {
                if (FoliageManager.Instance.Enabled)
                {
                    if (FoliageDB.sortedPrototypes[prototypeId].Enabled)
                    {
                        GenerateFoliageMeshInstanceForIndex(prototypeId, meshInstances.Key);
                    }
                }
            }
        }

        /// <summary>
        /// Create Foliage mesh instances for a certain index and foliage size.
        /// </summary>
        /// <param name="prototypeIndex"></param>
        /// <param name="resolution"></param>
        public static void GenerateFoliageMeshInstanceForIndex(int prototypeIndex, FoliageResolutions resolution)
        {
            Dictionary<int, FoliageGPUMesh> meshInstances = PrototypeMeshInstances[resolution];

            FoliagePrototype prototype = FoliageDB.sortedPrototypes[prototypeIndex];

            if (!FoliageManager.Instance.Enabled)
            {
                return;
            }

            bool prototypeMeshExists = PrototypeMeshInstances != null && meshInstances.ContainsKey(prototypeIndex);

            if (prototypeMeshExists)
            {
                meshInstances[prototypeIndex].Destroy();
                meshInstances.Remove(prototypeIndex);
            }

            Mesh[,,] meshes = null;
            List<byte> densities = prototype.DensitiesLods;
            FoliageMeshInstancesGroup instanceGroup = new FoliageMeshInstancesGroup();

            meshes = FoliageMeshInstance.CreateFoliageInstances(prototypeIndex, densities, out instanceGroup, resolution);

            meshInstances.Add(prototypeIndex, new FoliageGPUMesh(meshes, instanceGroup, prototypeIndex, resolution));
        }

        /// <summary>
        /// Destroy the current mesh instances.
        /// </summary>
        protected static void DestroyMeshInstances()
        {
            if (PrototypeMeshInstances.Count == 0)
            {
                return;
            }

            FoliagePrototype prototype;

            foreach (var meshInstances in PrototypeMeshInstances.Values)
            {
                for (int i = 0; i < FoliageDB.UnSortedPrototypes.Count; i++)
                {
                    prototype = FoliageDB.UnSortedPrototypes[i];

                    if (meshInstances != null && meshInstances.ContainsKey(prototype.Id))
                    {
                        meshInstances[prototype.Id].Destroy();
                    }
                }

                meshInstances.Clear();
            }

            s_PrototypeMeshInstances = null;
        }

        /// <summary>
        /// Destroy a mesh instance
        /// </summary>
        /// <param name="prototypeId"></param>
        public static void DestroyMeshInstance(int prototypeId)
        {
            foreach (var meshInstances in PrototypeMeshInstances.Values)
            {
                if (meshInstances != null && meshInstances.ContainsKey(prototypeId))
                {
                    meshInstances[prototypeId].Destroy();
                }

                meshInstances.Remove(prototypeId);
            }
        }

        /// <summary>
        /// Restart all of the queue instances.
        /// </summary>
        public static void RegenerateQueueInstances()
        {
            if (Application.isPlaying)
            {
                for (int i = 0; i < FoliageReceiver.FReceivers.Count; i++)
                {
                    FoliageReceiver.FReceivers[i].QueueReceiver = null;
                }
            }

#if UNITY_EDITOR
            FoliageManager.Instance.EditorQueueInstance = null;
#endif
        }

        /// <summary>
        /// Mark all of the densities as dirty
        /// </summary>
        public static void MarkDensitiesDirty()
        {
            if (Application.isPlaying)
            {
                for (int i = 0; i < FoliageReceiver.FReceivers.Count; i++)
                {
                    FoliageReceiver.FReceivers[i].QueueReceiver.ResetDensity();
                }
            }

#if UNITY_EDITOR
            RenderingQueueReceiver editorQueue = FoliageManager.Instance.EditorQueueInstance;
            
            if (editorQueue == null)
            {
                return; // no scene view visible.
            }
            
            editorQueue.ResetDensity();
#endif
        }

        public void OnGlobalPostRender(Camera camera_)
        {
#if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                SceneView sView = SceneView.currentDrawingSceneView;
                if (sView != null && sView.camera == camera_)
                {
                    if (Vector3.Distance(sView.camera.transform.position, RENDERING_lastEditorCameraPosition) > 5 || !RENDERING_editorDrawCalled)
                    {
                        EditorUtility.SetDirty(this);
            
                        RENDERING_lastEditorCameraPosition = sView.camera.transform.position;
                        RENDERING_editorDrawCalled = true;
                    }
                }
            }
#endif
        }

        protected override void Update()
        {
            base.Update();
            DrawEditorCameras();
        }

        /// <summary>
        /// 编辑器环境下渲染。
        /// </summary>
        private void DrawEditorCameras()
        {
#if UNITY_EDITOR
            SceneView sceneCamera = SceneView.lastActiveSceneView;
            
            if (sceneCamera == null)
            {
                return;
            }
            
            EditorQueueInstance.CheckPositionChange();
            
            RenderingPipielineCore.RenderQueue(EditorQueueInstance, sceneCamera.camera);
#endif
        }
    }
}
